我們在昨日了解封裝之後,今日我們要延伸到繼承與多型,以及抽象化
繼承的核心概念非常的簡單,就是父類別有的屬性和方法能夠接著做使用,可以重複利用程式碼,架構上也會比較完整且直觀。
讓我們來看一下範例程式碼:
class Father(object):
last_name = "Lin"
def __init__(self, name, height, weight, age):
self.__name = name
self.height = height
self.weight = weight
self.age = age
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = "new_name " + name
@staticmethod
def tool():
status = "打開"
return "{status} 麥克風".format(status=status)
def introduce_myself(self):
tool = Father.tool()
print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))
class Child(Father):
def __init__(self, name, height, weight, age):
super().__init__(name, height, weight, age)
self.friend = "Sam"
def introduce_myself(self):
print("我是 {name},我有朋友 {friend}".format(name=self.name, friend=self.friend))
if __name__ == '__main__':
Jacky = Father("Jacky", 180, 70, 58)
Jacky.introduce_myself() # 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡58 years old
son = Child("Jacky's son", 200, 80, 18)
son.introduce_myself() # 我是 Jacky's son,我有朋友 Sam
從程式碼中我們可以發現,我們這邊有個super.__init__
的功用是呼叫父類別的__init__(建構子)非常重要,也因為我們能夠呼叫父類別的建構子,所以我們能夠繼承並使用父類別的屬性,這樣能夠延伸、擴增架構,屬性方法也不需要重複寫。
以下面的程式碼為例:
class Father(object):
last_name = "Lin"
def __init__(self, name, height, weight, age):
self.__name = name
self.height = height
self.weight = weight
self.age = age
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = "new_name " + name
@staticmethod
def tool():
status = "打開"
return "{status} 麥克風".format(status=status)
def introduce_myself(self):
tool = Father.tool()
print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))
class Child(Father):
def __init__(self, name, height, weight, age):
# super().__init__(name, height, weight, age)
self.friend = "Sam"
def introduce_myself(self): # 複寫
print("我是 {name},我有朋友 {friend}".format(name=self.name, friend=self.friend))
if __name__ == '__main__':
Jacky = Father("Jacky", 180, 70, 58)
Jacky.introduce_myself() # 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡58 years old
son = Child("Jacky's son", 200, 80, 18)
son.introduce_myself() # AttributeError: 'Child' object has no attribute '_Father__name'
如果把32行註解掉,則會出現AttributeError: 'Child' object has no attribute '_Father__name'
的錯誤,所以一定要呼叫父類別的建構子,才能夠繼承父類別的實體屬性。
接著要來分享多型,那什麼是多型呢?多型分成「Override覆寫」、「Overload多載」。Override是因為繼承的關係,父類別與子類別有著兩個相同名稱的方法,但詳細做的事情不相同;Overload是在同一個類別底下,有著多個相同名稱的方法,但引入參數的型態各不同,那雖然有分這兩種用法,但是python裡沒有多載,在python裡,如果在同個類別底下宣告同樣名稱的方法,即使引入參數型態不同一樣會覆蓋前面的方法。
那我在24行和35行的地方有用到Override,雖然方法名稱一樣,但是在子類別下的方法可能做著更詳細的事情,像是不同人做同樣的事情,可是每個人詳細做法不同,大概是這樣一個概念。
最後我們來提一下抽象化,抽象化就是先寫出一個「架構」、「藍圖」、「設計圖」能夠讓人照著實作
我們來看看範例程式碼:
from abc import ABC, abstractmethod, abstractproperty
class Ancestor(ABC):
last_name = ""
name = ""
height = 0
age = 0
@staticmethod
@abstractmethod
def tool():
pass
@abstractmethod
def introduce_myself(self):
pass
class Father(Ancestor):
last_name = "Lin"
def __init__(self, name, height, weight, age):
self.Ancestor__name = name
self.height = height
self.weight = weight
self.age = age
@property
def name(self):
return self._Ancestor__name
@name.setter
def name(self):
return "new_name " + self._Ancestor__name
@staticmethod
def tool():
status = "打開"
return "{status} 麥克風".format(status=status)
def introduce_myself(self):
tool = Father.tool()
print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))
if __name__ == '__main__':
Jacky = Father("Jacky", 180, 70, 58)
Jacky.introduce_myself() # 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡58 years old
20-50行是前面繼承的例子,我在1-19行加上了一個抽象類別,透過抽象類別這樣一個介面、模組,讓Father這個Class去實作,以上面的程式碼為例,繼承抽象類別的子類別沒有實作introduce_myself方法的話,就會報TypeError: Can't instantiate abstract class Father with abstract methods introduce_myself
的錯誤。
這裡我在實作的時候遇到一些小小的狀況,記錄一下,就是我原本想在抽象設定寫建構子,宣告私有屬性,但是後來想到因為抽象類別本身就是設計給人繼承的,並「不會實例化」更不會有「建構子」,也就不會有「實例屬性」,再來是要給人繼承也不會宣告「私有屬性」,雖然在python底下還是可以使用_class__attribute這樣的方式去呼叫私有屬性,但是這是不對的,一般來說私有屬性是除了本身自己類別以外都無法使用,所以後來我全部改成使用「類別屬性且非私有」來寫。
特性 | 封裝 | 繼承 | 多型 | 抽象 |
---|---|---|---|---|
敘述 | 將詳細的實作內容以及資訊隱藏,限制特定類別存取屬性,避免該屬性被修改 | 將程式碼封裝後,透過繼承父類別的屬性,不但可以延伸、擴增架構,屬性與方法也不需要重複寫 | 繼承之後我們可以透過「覆寫」的用法,或者是「多個類別相同名稱的方法」,可以表達出每個「不同類別但同樣名稱的方法」實作的詳細內容 | 寫出一個「架構」、「藍圖」、「設計圖」能夠讓人照著實作 |
注意事項 | 限制存取屬性可以使用property | super可以呼叫父類別的屬性,沒有super.__init__的話,子類別依然可以繼承並使用父類別屬性,要注意繼承屬性的權限(public/ protected/ private) | 多型有「覆寫」、「多載」python不支援多載 | 不會有建構子,也就不會有實例屬性,不會有私有屬性 |
物件導向的特性就分享到這邊告一段落,明天我要接著要來介紹封裝